package com.example.apisimpledy.aspect;

import com.example.apisimpledy.entity.dto.Log;
import com.example.apisimpledy.entity.dto.MyUser;
import com.example.apisimpledy.repository.LogRepository;
import com.example.apisimpledy.repository.MyUserRepository;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.sql.Timestamp;
import java.time.Instant;
import java.util.Optional;

@Aspect
@Component
public class RequestLoggingAspect {

    @Autowired
    private LogRepository logRepository;

    @Autowired
    private MyUserRepository myUserRepository;

    @Pointcut("execution(* com.example.apisimpledy.controller.*.*(..))")
    public void controllerMethods() {}

    @Before("controllerMethods()")
    public void logBefore(JoinPoint joinPoint) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return;
        }

        String username = authentication.getName();
        Optional<MyUser> userOptional = myUserRepository.findByUsernameOrEmail(username, username);

        if (userOptional.isEmpty()) {
            return;
        }

        MyUser user = userOptional.get();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String methodName = methodSignature.getName();
        Object[] args = joinPoint.getArgs();
        StringBuilder input = new StringBuilder();
        for (Object arg : args) {
            input.append(arg.toString()).append(" ");
        }

        Log log = new Log();
        log.setMyUser(user);
        log.setAction("Request: " + methodName);
        log.setTimestamp(Timestamp.from(Instant.now()));
        log.setDetails("Input: " + input.toString().trim());

        logRepository.save(log);
    }

    @AfterReturning(pointcut = "controllerMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return;
        }

        String username = authentication.getName();
        Optional<MyUser> userOptional = myUserRepository.findByUsernameOrEmail(username, username);

        if (userOptional.isEmpty()) {
            return;
        }

        MyUser user = userOptional.get();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String methodName = methodSignature.getName();
        String output = result != null ? result.toString() : "null";

        Log log = new Log();
        log.setMyUser(user);
        log.setAction("Response: " + methodName);
        log.setTimestamp(Timestamp.from(Instant.now()));
        log.setDetails("Output: " + output);

        logRepository.save(log);
    }

    @AfterThrowing(pointcut = "controllerMethods()", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return;
        }

        String username = authentication.getName();
        Optional<MyUser> userOptional = myUserRepository.findByUsernameOrEmail(username, username);

        if (userOptional.isEmpty()) {
            return;
        }

        MyUser user = userOptional.get();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String methodName = methodSignature.getName();

        Log log = new Log();
        log.setMyUser(user);
        log.setAction("Exception in: " + methodName);
        log.setTimestamp(Timestamp.from(Instant.now()));
        log.setDetails("Error: " + error.getMessage());

        logRepository.save(log);
    }

    @Around("controllerMethods()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            String username = authentication.getName();
            Optional<MyUser> userOptional = myUserRepository.findByUsernameOrEmail(username, username);

            if (userOptional.isPresent()) {
                MyUser user = userOptional.get();
                MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
                String methodName = methodSignature.getName();

                Log log = new Log();
                log.setMyUser(user);
                log.setAction("Execution time for: " + methodName);
                log.setTimestamp(Timestamp.from(Instant.now()));
                log.setDetails("Execution time: " + executionTime + "ms");

                logRepository.save(log);
            }
        }

        return proceed;
    }
}
